#include <iostream>
#include <cmath>

#if 0
    class Point
    {
    public:
        // 无参构造
        Point():x(0), y(0){}
        // 带参构造 初始化列表
        Point(int x, int y):x(x),y(y){}

        void show()
        {
            std::cout<<"("<<x<<","<<y<<")"<<std::endl;
        }

    private:
        int x;
        int y;
    };

    int main()
    {
        Point p;
        p.show();
        Point p1(1, 2);
        p1.show();

        return 0;
    }
#endif

// 友元
// 作用：在不破坏原有类的封装性的情况下，可以在类的外部访问它的私有成员
#if 0
    // 友元函数
    class Point
    {
    public:
        // 无参构造
        Point():x(0), y(0){}
        // 带参构造 初始化列表
        Point(int x, int y):x(x),y(y){}

        void show()
        {
            std::cout<<"("<<x<<","<<y<<")"<<std::endl;
        }

        // 友元函数的声明（表示这个函数是这个类的朋友，所以能访问私有成员）
        friend double Distance (const Point &p1, const Point &p2);       

    private:
        int x;
        int y;
    };

    // 计算两点之间的距离
    // 加const：为了防止该对象在函数中被修改
    // 传引用：减少拷贝
    double Distance(const Point &p1, const Point &p2)
    {
        // 拿不到横纵坐标（私有成员）
        // 这个类不要再去动它，也就是不要再去给它加任何接口，因为可能这个类在别的地方已经用过了
        // 所以现在面临一个困境：要给这个类的对象添加全新的功能，但是处理不了，因为必须要访问它的私有成员，在这种情况下，引进了关键字——友元（friend）
        return std::sqrt(std::pow(p1.x - p2.x, 2) + std::pow(p1.y - p2.y, 2)); // sqrt: 开方，pow: 次方
    }

    int main()
    {
        Point p1;
        p1.show();
        Point p2(1, 1);
        p2.show();

        std::cout<<Distance(p1, p2)<<std::endl;
        return 0;
    }
#endif

#if 1
    // 友元类
    // 当有新的需求时，可能还会用到类的私有成员，这样类里的友元声明就会越积越多，会让这个类变得非常臃肿，这样不利于我们去维护一个类
    // 既然这么多函数都要用到它的私有成员，那就把这些函数都集合到一个类里，然后把这个类声明为它的友元
    // 单一职责原则：一个类只做它最应该做的最小责任事情
    class Point
    {
    public:
        // 无参构造
        Point():x(0), y(0){}
        // 带参构造 初始化列表
        Point(int x, int y):x(x),y(y){}

        void show()
        {
            std::cout<<"("<<x<<","<<y<<")"<<std::endl;
        }

        // 友元类的声明
        friend class Tool;

    private:
        int x;
        int y;
    };

    class Tool
    {
    public:
        // 计算点到原点的距离
        // 如果要调用这个函数还得定义一个它的对象，这就不符合工具类的定义，工具类就是用来辅助的一些函数的集合，结果为了调用这个函数还得专门定义这个类的对象，没必要
        // 所以一般会把这些函数定成静态的，静态的就不需要调对象了，调用时加个作用域就可以了
        static double Distance2Zero(const Point &p1)
        {
            return std::sqrt(std::pow(p1.x, 2) + std::pow(p1.y, 2));
        }
    };

    int main()
    {
        Point p1;
        p1.show();
        Point p2(2, 1);
        p2.show();

        std::cout<<Tool::Distance2Zero(p2)<<std::endl; // 调用时加个作用域就可以了，不用特地创建个类的对象，再通过对象去调
        return 0;
    }
#endif